//*****************************************************************************
//
// Microsoft Windows Media
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// FileName:            GenProfile_lib.cpp
//
// Abstract:            The implementation for the GenProfile static library.
//
//*****************************************************************************

#include "stdafx.h"
#include <assert.h>
#include "GenProfile_lib.h"
#include "Twmv9dll.h"

#define SAFE_RELEASE( x )   \
	if ( x )                \
	{                       \
	x->Release();       \
	x = NULL;           \
	}

#define SAFE_ADDREF( x )    \
	if ( x )                \
	{                       \
	x->AddRef();        \
	}

#define SAFE_DELETE( x )    \
	if ( x )                \
	{                       \
	delete x;           \
	x = NULL;           \
	}

#define SAFE_ARRAYDELETE( x )   \
	if ( x )                    \
	{                           \
	delete[] x;             \
	x = NULL;               \
	}

#define SAFE_SYSFREESTRING( x ) \
	if ( x )                    \
	{                           \
	SysFreeString( x );     \
	x = NULL;               \
	}

#define SAFE_CLOSEHANDLE( x )               \
	if ( x && INVALID_HANDLE_VALUE != x )   \
	{                                       \
	CloseHandle( x );                   \
	x = NULL;                           \
	}

struct PIXEL_FORMAT
{
	const GUID* guidFormat;
	DWORD dwFourCC;
	WORD wBitsPerPixel;
};

PIXEL_FORMAT PixelFormats[] =
{
	{ &WMMEDIASUBTYPE_RGB555,   BI_RGB,         16 },
	{ &WMMEDIASUBTYPE_RGB24,    BI_RGB,         24 },
	{ &WMMEDIASUBTYPE_RGB32,    BI_RGB,         32 },
	{ &WMMEDIASUBTYPE_I420,     0x30323449,     12 },
	{ &WMMEDIASUBTYPE_IYUV,     0x56555949,     12 },
	{ &WMMEDIASUBTYPE_YV12,     0x32315659,     12 },
	{ &WMMEDIASUBTYPE_YUY2,     0x32595559,     16 },
	{ &WMMEDIASUBTYPE_UYVY,     0x59565955,     16 },
	{ &WMMEDIASUBTYPE_YVYU,     0x55595659,     16 }
};


DWORD WaveFrequency[] =
{
	8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
};


//------------------------------------------------------------------------------
// Name: CopyMediaType()
// Desc: Allocates memory for a WM_MEDIA_TYPE and its format data and
//       copies an existing media type into it.
//------------------------------------------------------------------------------
STDMETHODIMP CopyMediaType( WM_MEDIA_TYPE** ppmtDestination,
						   WM_MEDIA_TYPE* pmtSource )
{
	if ( !ppmtDestination )
	{
		return E_POINTER;
	}
	if ( !pmtSource )
	{
		return E_NOTIMPL;
	}

	//
	// Create enough space for the media type and its format data
	//
	*ppmtDestination = (WM_MEDIA_TYPE*) new BYTE[ sizeof( WM_MEDIA_TYPE ) + pmtSource->cbFormat ];
	if ( !*ppmtDestination)
	{
		return E_OUTOFMEMORY;
	}

	//
	// Copy the media type and the format data
	//
	memcpy( *ppmtDestination, pmtSource, sizeof( WM_MEDIA_TYPE ) );
	(*ppmtDestination)->pbFormat = ( ((BYTE*) *ppmtDestination) + sizeof( WM_MEDIA_TYPE ) ); // Format data is immediately after media type
	memcpy( (*ppmtDestination)->pbFormat, pmtSource->pbFormat, pmtSource->cbFormat );

	return S_OK;
}


//------------------------------------------------------------------------------
// Name: EnsureIWMCodecInfo3()
// Desc: Creates an IWMCodecInfo3 interface if none exists, and ensures
//       an outstanding reference either way.  This way the IWMCodecInfo3
//       object is guaranteed to exist and isn't released too many times.
//------------------------------------------------------------------------------
STDMETHODIMP EnsureIWMCodecInfo3( Twmv9dll *dll,IWMCodecInfo3** ppCodecInfo3 )
{
	HRESULT hr = S_OK;

	if ( !ppCodecInfo3 )
	{
		return E_POINTER;
	}

	do
	{
		if ( !*ppCodecInfo3 )
		{
			//
			// Create a new IWMCodecInfo3 object
			//
			IWMProfileManager* pProfileManager;
			hr = dll->WMCreateProfileManager( &pProfileManager );
			if ( FAILED( hr ) )
			{
				break;
			}
			assert( pProfileManager );

			hr = pProfileManager->QueryInterface( IID_IWMCodecInfo3, (void**) ppCodecInfo3 );
			SAFE_RELEASE( pProfileManager );
			if ( FAILED( hr ) )
			{
				break;
			}
		}
		else
		{
			//
			// Add a reference to the existing object, so that it won't be destroyed during cleanup
			//
			SAFE_ADDREF( (*ppCodecInfo3) );
		}
		assert( *ppCodecInfo3 );

		//
		// It should now not matter if the IWMCodecInfo3 was just created or was passed in
		//
	}
	while ( FALSE );

	return hr;
}


//------------------------------------------------------------------------------
// Name: SetCodecVBRSettings()
// Desc: Enables VBR with the specified number of passes, or disables it.
//------------------------------------------------------------------------------
STDMETHODIMP SetCodecVBRSettings( IWMCodecInfo3* pCodecInfo3,
								 GUID guidCodecType,
								 DWORD dwCodecIndex,
								 BOOL fIsVBR,
								 DWORD dwVBRPasses )
{
	HRESULT hr;

	if ( !pCodecInfo3 )
	{
		return E_INVALIDARG;
	}

	do
	{
		//
		// Configure the codec to use or not use VBR as requested
		//
		hr = pCodecInfo3->SetCodecEnumerationSetting( guidCodecType, dwCodecIndex, g_wszVBREnabled, WMT_TYPE_BOOL, (BYTE*) &fIsVBR, sizeof( BOOL ) );
		if ( FAILED( hr ) )
		{
			//
			// If VBR is requested, then it's a problem, but otherwise the codec may just not support VBR
			//
			if ( ( !fIsVBR ) && ( NS_E_UNSUPPORTED_PROPERTY == hr ) )
			{
				hr = S_OK;
			}
			else
			{
				break;
			}
		}

		if ( fIsVBR )
		{
			hr = pCodecInfo3->SetCodecEnumerationSetting( guidCodecType, dwCodecIndex, g_wszNumPasses, WMT_TYPE_DWORD, (BYTE*) &dwVBRPasses, sizeof( DWORD ) );
			if ( FAILED( hr ) )
			{
				break;
			}
		}

	}
	while ( FALSE );

	return hr;
}


//------------------------------------------------------------------------------
// Name: SetStreamLanguage()
// Desc: Sets the language in the stream configuration.
//------------------------------------------------------------------------------
/*
STDMETHODIMP SetStreamLanguage( IWMStreamConfig * pStreamConfig, LCID dwLanguage )
{
HRESULT hr = S_OK;
IWMStreamConfig3 * pStreamConfig3 = NULL;
IMultiLanguage * pMLang = NULL;
BSTR bstrLanguage = NULL;

do
{
hr = CoCreateInstance( CLSID_CMultiLanguage,
NULL,
CLSCTX_ALL,
IID_IMultiLanguage,
(VOID **) &pMLang );

if( FAILED( hr ) )
{
break;
}

hr = pMLang->GetRfc1766FromLcid( dwLanguage, &bstrLanguage );
if( FAILED( hr ) )
{
break;
}

hr = pStreamConfig->QueryInterface(	IID_IWMStreamConfig3, (void**)&pStreamConfig3 );
if(	FAILED(	hr ) )
{
break;
}

hr = pStreamConfig3->SetLanguage( bstrLanguage );
if(	FAILED(	hr ) )
{
break;
}
}
while (FALSE);

SAFE_RELEASE( pMLang );
SAFE_RELEASE( pStreamConfig3 );

if ( !bstrLanguage )
{
SysFreeString( bstrLanguage );
}

return hr;
}
*/

/*
** Functions that create media types for the various stream types
*/

//------------------------------------------------------------------------------
// Name: CreateUncompressedAudioMediaType()
// Desc: Initializes a WM_MEDIA_TYPE for uncompressed audio.
//------------------------------------------------------------------------------
STDMETHODIMP CreateUncompressedAudioMediaType(  WM_MEDIA_TYPE** ppmtMediaType,
											  DWORD dwSamplesPerSecond,
											  WORD wNumChannels,
											  WORD wBitsPerSample )
{
	HRESULT hr = S_OK;

	WM_MEDIA_TYPE mtUncompressedAudio;
	WAVEFORMATEX wfxUncompressedAudio;

	if ( !ppmtMediaType )
	{
		return E_POINTER;
	}
	//
	// pCodecInfo3 is allowed to be NULL, since CreateMediatypeForFormat calls EnsureIWMCodecInfo3
	//

	do
	{
		//
		// Setup the local copy of the uncompressed media type
		//
		ZeroMemory( &mtUncompressedAudio, sizeof( mtUncompressedAudio ) );

		mtUncompressedAudio.majortype = WMMEDIATYPE_Audio;
		mtUncompressedAudio.subtype = WMMEDIASUBTYPE_PCM;
		mtUncompressedAudio.bFixedSizeSamples = TRUE;
		mtUncompressedAudio.bTemporalCompression = FALSE;
		mtUncompressedAudio.lSampleSize = wNumChannels * wBitsPerSample / 8;
		mtUncompressedAudio.formattype = WMFORMAT_WaveFormatEx;
		mtUncompressedAudio.pUnk = NULL;
		mtUncompressedAudio.cbFormat = sizeof( WAVEFORMATEX );
		mtUncompressedAudio.pbFormat = (BYTE*) &wfxUncompressedAudio;

		//
		// Configure the WAVEFORMATEX structure for the uncompressed audio
		//
		ZeroMemory( &wfxUncompressedAudio, sizeof( wfxUncompressedAudio ) );

		wfxUncompressedAudio.wFormatTag = 1;
		wfxUncompressedAudio.nChannels = wNumChannels;
		wfxUncompressedAudio.nSamplesPerSec = dwSamplesPerSecond;
		wfxUncompressedAudio.nAvgBytesPerSec = dwSamplesPerSecond * ( wNumChannels * wBitsPerSample / 8 );
		wfxUncompressedAudio.nBlockAlign = wNumChannels * wBitsPerSample / 8;
		wfxUncompressedAudio.wBitsPerSample = wBitsPerSample;
		wfxUncompressedAudio.cbSize = sizeof( WAVEFORMATEX );

		//
		// Return a copy of the media type to the caller, since the mediatype is on the stack
		//
		hr = CopyMediaType( ppmtMediaType, &mtUncompressedAudio );
		if ( FAILED( hr ) )
		{
			break;
		}
	}
	while( FALSE );

	return( hr );
}


//------------------------------------------------------------------------------
// Name: CreateVideoMediaType()
// Desc: Initializes a WM_MEDIA_TYPE for video.
//------------------------------------------------------------------------------
STDMETHODIMP CreateVideoMediaType(  Twmv9dll *dll,
								  WM_MEDIA_TYPE** ppmtMediaType,
								  IWMCodecInfo3* pCodecInfo3,
								  DWORD dwCodecIndex,
								  double dFPS,
								  DWORD dwWidth,
								  DWORD dwHeight,
								  DWORD dwBitrate,
								  BOOL fIsVBR,
								  DWORD dwNumberOfPasses )
{
	HRESULT hr = S_OK;

	WM_MEDIA_TYPE *pMediaType = NULL;
	WMVIDEOINFOHEADER *pVIH;

	if ( !ppmtMediaType )
	{
		return E_POINTER;
	}
	//
	// pCodecInfo3 is allowed to be NULL, since CreateMediatypeForFormat calls EnsureIWMCodecInfo3
	//

	do
	{
		//
		// Get the mediatype for the codec
		//
		hr = CreateMediatypeForFormat( dll,
			&pMediaType,
			pCodecInfo3,
			NULL,
			WMMEDIATYPE_Video,
			dwCodecIndex,
			0,
			fIsVBR,
			dwNumberOfPasses );
		if( FAILED( hr ) )
		{
			break;
		}
		assert( pMediaType );

		//
		// Configure the WMVIDEOINFOHEADER structure of the media type
		//
		pVIH = (WMVIDEOINFOHEADER*) pMediaType->pbFormat;
		pVIH->dwBitRate = dwBitrate;

		pVIH->rcSource.right = dwWidth;
		pVIH->rcSource.bottom = dwHeight;
		pVIH->rcTarget.right = dwWidth;
		pVIH->rcTarget.bottom = dwHeight;
		pVIH->bmiHeader.biWidth = dwWidth;
		pVIH->bmiHeader.biHeight = dwHeight;

		pVIH->AvgTimePerFrame = ( (LONGLONG) 10000000 ) / ( (LONGLONG) dFPS );

		//
		// Return a copy of the media type to the caller
		//
		hr = CopyMediaType( ppmtMediaType, pMediaType );
		if ( FAILED( hr ) )
		{
			break;
		}
	}
	while( FALSE );

	SAFE_ARRAYDELETE( pMediaType );

	return( hr );
}


//------------------------------------------------------------------------------
// Name: CreateUncompressedVideoMediaType()
// Desc: Initializes a WM_MEDIA_TYPE for uncompressed video.
//------------------------------------------------------------------------------
STDMETHODIMP CreateUncompressedVideoMediaType(  WM_MEDIA_TYPE** ppmtMediaType,
											  GUID guidFormat,
											  DWORD dwFourCC,
											  WORD wBitsPerPixel,
											  BYTE* pbPaletteData,
											  DWORD cbPaletteDataSize,
											  DWORD dwFPS,
											  DWORD dwWidth,
											  DWORD dwHeight )
{
	const DWORD BITFIELD_DATA_SIZE = sizeof( RGBQUAD ) * 3;

	HRESULT hr;
	WM_MEDIA_TYPE mtUncompressedVideo;
	WMVIDEOINFOHEADER* pvihUncompressedVideo;
	BYTE* pbFormatData = NULL;
	DWORD dwFormatDataSize;
	DWORD dwMaxColors;
	BOOL fPalettePresent;
	BYTE* pbPostVIHData;
	DWORD cbExpectedPostVIHDataSize;
	BOOL fBitfieldsPresent;

	if ( !ppmtMediaType )
	{
		return E_POINTER;
	}
	//
	// pCodecInfo3 is allowed to be NULL, since CreateMediatypeForFormat calls EnsureIWMCodecInfo3
	//

	//
	// The width must be on a byte boundry
	//
	if ( dwWidth * wBitsPerPixel % 8 != 0 )
	{
		return E_INVALIDARG;
	}

	//
	// The width, height, and frames per second must all be non-zero
	//
	if ( 0 == dwWidth || 0 == dwHeight  || 0 == dwFPS )
	{
		return E_INVALIDARG;
	}

	do
	{
		//
		// Allocate space for the format data ( WMVIDEOINFOHEADER + pallete data )
		//
		dwFormatDataSize = sizeof( WMVIDEOINFOHEADER );
		cbExpectedPostVIHDataSize = 0;

		//
		// If there are <= 8 bits / pixel, then there needs to be palette data following the WMVIDEOINFOHEADER
		//
		fPalettePresent = ( wBitsPerPixel <= 8 );
		if ( fPalettePresent )
		{
			dwMaxColors = 1 << wBitsPerPixel;
			cbExpectedPostVIHDataSize = sizeof( RGBQUAD ) * dwMaxColors;
		}

		//
		// If the format uses bitfields, then make sure the data is following
		//
		fBitfieldsPresent = ( BI_BITFIELDS == dwFourCC );
		if ( fBitfieldsPresent )
		{
			cbExpectedPostVIHDataSize = BITFIELD_DATA_SIZE;
		}

		if ( fPalettePresent || fBitfieldsPresent )
		{
			dwFormatDataSize += cbExpectedPostVIHDataSize;
			if ( !pbPaletteData || ( cbPaletteDataSize != cbExpectedPostVIHDataSize ) )
			{
				hr = E_INVALIDARG;
				break;
			}
		}

		pbFormatData = new BYTE[ dwFormatDataSize ];
		if ( !pbFormatData )
		{
			hr = E_OUTOFMEMORY;
			break;
		}
		ZeroMemory( pbFormatData, dwFormatDataSize );

		pbPostVIHData = pbFormatData + sizeof( WMVIDEOINFOHEADER );
		pvihUncompressedVideo = (WMVIDEOINFOHEADER*) pbFormatData;

		//
		// Set up the local copy of the uncompressed media type
		//
		ZeroMemory( &mtUncompressedVideo, sizeof( mtUncompressedVideo ) );

		mtUncompressedVideo.majortype = WMMEDIATYPE_Video;
		mtUncompressedVideo.subtype = guidFormat;
		mtUncompressedVideo.bFixedSizeSamples = TRUE;
		mtUncompressedVideo.bTemporalCompression = FALSE;
		mtUncompressedVideo.lSampleSize = wBitsPerPixel * dwWidth * dwHeight / 8;
		mtUncompressedVideo.formattype = WMFORMAT_VideoInfo;
		mtUncompressedVideo.pUnk = NULL;
		mtUncompressedVideo.cbFormat = dwFormatDataSize;
		mtUncompressedVideo.pbFormat = (BYTE*) pbFormatData;

		//
		// Configure the WMVIDEOINFOHEADER structure for uncompressed video
		//
		pvihUncompressedVideo->dwBitRate = mtUncompressedVideo.lSampleSize * dwFPS * 8;

		pvihUncompressedVideo->rcSource.right = dwWidth;
		pvihUncompressedVideo->rcSource.bottom = dwHeight;
		pvihUncompressedVideo->rcTarget.right = dwWidth;
		pvihUncompressedVideo->rcTarget.bottom = dwHeight;


		pvihUncompressedVideo->bmiHeader.biSizeImage = mtUncompressedVideo.lSampleSize;
		pvihUncompressedVideo->bmiHeader.biPlanes = 1;
		pvihUncompressedVideo->bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
		pvihUncompressedVideo->bmiHeader.biWidth = dwWidth;
		pvihUncompressedVideo->bmiHeader.biHeight = dwHeight;
		pvihUncompressedVideo->bmiHeader.biCompression = dwFourCC;
		pvihUncompressedVideo->bmiHeader.biBitCount = wBitsPerPixel;

		pvihUncompressedVideo->AvgTimePerFrame = ( (LONGLONG) 10000000 ) / ( (LONGLONG) dwFPS );

		//
		// Copy the palette information, if present
		//
		if ( ( fPalettePresent || fBitfieldsPresent ) && pbPaletteData )
		{
			memcpy( pbPostVIHData, pbPaletteData, cbExpectedPostVIHDataSize );
		}

		//
		// Return a copy of the media type to the caller, since the media type is on the stack
		//
		hr = CopyMediaType( ppmtMediaType, &mtUncompressedVideo );
		if ( FAILED( hr ) )
		{
			break;
		}
	}
	while( FALSE );

	SAFE_ARRAYDELETE( pbFormatData );

	return( hr );
}


//------------------------------------------------------------------------------
// Name: CreateScriptMediaType()
// Desc: Initializes a WM_MEDIA_TYPE for script.
//------------------------------------------------------------------------------
STDMETHODIMP CreateScriptMediaType(  WM_MEDIA_TYPE** ppmtMediaType )
{
	HRESULT hr = S_OK;
	WM_MEDIA_TYPE wmtMediaType;
	WMSCRIPTFORMAT wsfScriptFormat;

	assert( ppmtMediaType );

	do
	{
		ZeroMemory( &wmtMediaType, sizeof( wmtMediaType ) );

		//
		// Configure media type
		//
		wmtMediaType.majortype              = WMMEDIATYPE_Script;
		wmtMediaType.subtype                = GUID_NULL;
		wmtMediaType.bFixedSizeSamples      = FALSE;
		wmtMediaType.bTemporalCompression   = TRUE;
		wmtMediaType.lSampleSize            = 0;
		wmtMediaType.formattype             = WMFORMAT_Script;
		wmtMediaType.cbFormat               = sizeof( WMSCRIPTFORMAT );
		wmtMediaType.pbFormat               = (BYTE*) &wsfScriptFormat;

		wsfScriptFormat.scriptType          = WMSCRIPTTYPE_TwoStrings;

		//
		// Return a copy of the media type to the caller
		//
		hr = CopyMediaType( ppmtMediaType, &wmtMediaType );
		if ( FAILED( hr ) )
		{
			break;
		}
	}
	while ( FALSE );

	return hr;
}


//------------------------------------------------------------------------------
// Name: CreateImageMediaType()
// Desc: Initializes a WM_MEDIA_TYPE for image.
//------------------------------------------------------------------------------
STDMETHODIMP CreateImageMediaType(  WM_MEDIA_TYPE** ppmtMediaType,
								  DWORD dwWidth,
								  DWORD dwHeight,
								  DWORD dwBitrate )
{
	static const WORD BIT_COUNT = 24;

	HRESULT hr = S_OK;
	WM_MEDIA_TYPE wmtMediaType;
	WMVIDEOINFOHEADER vihVideoInfo;

	do
	{
		ZeroMemory( &wmtMediaType, sizeof( wmtMediaType ) );

		//
		// Set up the WM_MEDIA_TYPE structure
		//
		wmtMediaType.majortype              = WMMEDIATYPE_Image;
		wmtMediaType.subtype                = WMMEDIASUBTYPE_RGB24;
		wmtMediaType.bFixedSizeSamples      = FALSE;
		wmtMediaType.bTemporalCompression   = FALSE;
		wmtMediaType.lSampleSize			= 0;
		wmtMediaType.bFixedSizeSamples      = FALSE;
		wmtMediaType.bTemporalCompression   = FALSE;
		wmtMediaType.lSampleSize            = 0;

		wmtMediaType.formattype             = WMFORMAT_VideoInfo;
		wmtMediaType.pUnk                   = NULL;
		wmtMediaType.cbFormat               = sizeof( WMVIDEOINFOHEADER );
		wmtMediaType.pbFormat               = (BYTE*) &vihVideoInfo;

		//
		// Set up the WMVIDEOINFOHEADER structure
		//
		ZeroMemory( &vihVideoInfo, sizeof( vihVideoInfo ) );

		vihVideoInfo.rcSource.left = 0;
		vihVideoInfo.rcSource.top = 0;
		vihVideoInfo.rcSource.bottom = dwHeight;
		vihVideoInfo.rcSource.right = dwWidth;
		vihVideoInfo.rcTarget = vihVideoInfo.rcSource;

		vihVideoInfo.dwBitRate = dwBitrate;
		vihVideoInfo.dwBitErrorRate = 0;
		vihVideoInfo.AvgTimePerFrame = 0;

		vihVideoInfo.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
		vihVideoInfo.bmiHeader.biWidth = dwWidth;
		vihVideoInfo.bmiHeader.biHeight = dwHeight;
		vihVideoInfo.bmiHeader.biPlanes = 1;
		vihVideoInfo.bmiHeader.biBitCount = BIT_COUNT;
		vihVideoInfo.bmiHeader.biCompression = BI_RGB;
		vihVideoInfo.bmiHeader.biSizeImage = ( dwHeight * dwWidth * vihVideoInfo.bmiHeader.biBitCount ) / 8;
		vihVideoInfo.bmiHeader.biXPelsPerMeter = 0;
		vihVideoInfo.bmiHeader.biYPelsPerMeter = 0;
		vihVideoInfo.bmiHeader.biClrUsed = 0;
		vihVideoInfo.bmiHeader.biClrImportant = 0;

		//
		// Return a copy of the media type to the caller
		//
		hr = CopyMediaType( ppmtMediaType, &wmtMediaType );
		if ( FAILED( hr ) )
		{
			break;
		}
	}
	while ( FALSE );

	return hr;
}


//------------------------------------------------------------------------------
// Name: CreateWebMediaType()
// Desc: Initializes a WM_MEDIA_TYPE for Web media.
//------------------------------------------------------------------------------
STDMETHODIMP CreateWebMediaType(  WM_MEDIA_TYPE** ppmtMediaType )
{
	HRESULT hr = S_OK;
	WM_MEDIA_TYPE wmtMediaType;
	WMT_WEBSTREAM_FORMAT wwfWebFormat;

	do
	{
		ZeroMemory( &wmtMediaType, sizeof( wmtMediaType ) );

		//
		// Configure media type
		//
		wmtMediaType.majortype              = WMMEDIATYPE_FileTransfer;
		wmtMediaType.subtype                = WMMEDIASUBTYPE_WebStream;
		wmtMediaType.bFixedSizeSamples      = FALSE;
		wmtMediaType.bTemporalCompression   = TRUE;
		wmtMediaType.lSampleSize            = 0;
		wmtMediaType.formattype             = WMFORMAT_WebStream;
		wmtMediaType.pUnk                   = NULL;
		wmtMediaType.cbFormat               = sizeof( WMT_WEBSTREAM_FORMAT );
		wmtMediaType.pbFormat               = (BYTE*) &wwfWebFormat;

		ZeroMemory( &wwfWebFormat, sizeof( wwfWebFormat ) );
		wwfWebFormat.cbSize = sizeof( WMT_WEBSTREAM_FORMAT );
		wwfWebFormat.cbSampleHeaderFixedData = sizeof( WMT_WEBSTREAM_SAMPLE_HEADER );
		wwfWebFormat.wVersion = 1;
		wwfWebFormat.wReserved = 0;

		//
		// Return a copy of the media type to the caller
		//
		hr = CopyMediaType( ppmtMediaType, &wmtMediaType );
		if ( FAILED( hr ) )
		{
			break;
		}
	}
	while ( FALSE );

	return hr;
}


//------------------------------------------------------------------------------
// Name: CreateFileMediaType()
// Desc: Initializes a WM_MEDIA_TYPE for file transfer.
//------------------------------------------------------------------------------
STDMETHODIMP CreateFileMediaType(  WM_MEDIA_TYPE** ppmtMediaType )
{
	HRESULT hr = S_OK;
	WM_MEDIA_TYPE wmtMediaType;

	do
	{
		ZeroMemory( &wmtMediaType, sizeof( wmtMediaType ) );

		//
		// Configure media type
		//
		wmtMediaType.majortype              = WMMEDIATYPE_FileTransfer;
		wmtMediaType.subtype                = GUID_NULL;
		wmtMediaType.bFixedSizeSamples      = FALSE;
		wmtMediaType.bTemporalCompression   = FALSE;
		wmtMediaType.lSampleSize            = 0;

		//
		// Return a copy of the media type to the caller
		//
		hr = CopyMediaType( ppmtMediaType, &wmtMediaType );
		if ( FAILED( hr ) )
		{
			break;
		}
	}
	while ( FALSE );

	return hr;
}

//------------------------------------------------------------------------------
// Name: CreateVideoStream()
// Desc: Creates a video stream and returns its configuration object.
//------------------------------------------------------------------------------
STDMETHODIMP CreateVideoStream( Twmv9dll *dll,
							   IWMStreamConfig** ppStreamConfig,
							   IWMCodecInfo3* pCodecInfo3,
							   IWMProfile *pProfile,
							   DWORD dwCodecIndex,
							   DWORD dwBitrate,
							   DWORD dwBufferWindow,
							   DWORD dwWidth,
							   DWORD dwHeight,
							   double dFPS,
							   DWORD dwQuality,
							   DWORD dwSecPerKey,
							   BOOL fIsVBR,
							   VIDEO_VBR_MODE vbrMode,
							   DWORD dwVBRQuality,
							   DWORD dwMaxBitrate,
							   DWORD dwMaxBufferWindow,
							   LCID dwLanguage  )
{
	HRESULT hr = S_OK;
	IWMStreamConfig* pStreamConfig = NULL;
	IWMPropertyVault* pPropertyVault = NULL;
	WM_MEDIA_TYPE* pMediaType = NULL;
	IWMVideoMediaProps* pVideoMediaProps = NULL;
	DWORD dwNumberOfPasses;

	if ( !ppStreamConfig )
	{
		return E_POINTER;
	}
	if ( !pProfile )
	{
		return E_INVALIDARG;
	}
	//
	// pCodecInfo3 is allowed to be NULL, since CreateVideoMediatype calls EnsureIWMCodecInfo3
	//

	do
	{
		switch( vbrMode )
		{
		case VBR_OFF:
			dwNumberOfPasses = 0;
			break;

		case VBR_QUALITYBASED:
			dwNumberOfPasses = 1;
			break;

		case VBR_CONSTRAINED:
			dwNumberOfPasses = 2;
			break;

		case VBR_UNCONSTRAINED:
			dwNumberOfPasses = 2;
			break;

		default:
			hr = E_FAIL;
			break;
		}
		if ( FAILED( hr ) )
		{
			break;
		}

		//
		// Create the video stream
		//
		hr = pProfile->CreateNewStream( WMMEDIATYPE_Video, &pStreamConfig );
		if ( FAILED( hr ) )
		{
			break;
		}
		assert( pStreamConfig );

		//
		// Configure the new video stream
		//
		hr = pStreamConfig->SetBitrate( dwBitrate );
		if ( FAILED( hr ) )
		{
			break;
		}

		hr = pStreamConfig->SetBufferWindow( dwBufferWindow );
		if ( FAILED( hr ) )
		{
			break;
		}

		//
		// Set the media type for the stream
		//
		hr = CreateVideoMediaType(  dll,
			&pMediaType,
			pCodecInfo3,
			dwCodecIndex,
			dFPS,
			dwWidth,
			dwHeight,
			dwBitrate,
			fIsVBR,
			dwNumberOfPasses );
		if ( FAILED( hr ) )
		{
			break;
		}
		assert( pMediaType );

		hr = pStreamConfig->QueryInterface( IID_IWMVideoMediaProps, (void**) &pVideoMediaProps );
		if ( FAILED( hr ) )
		{
			break;
		}
		assert( pVideoMediaProps );

		hr = pVideoMediaProps->SetMediaType( pMediaType );
		if ( FAILED( hr ) )
		{
			break;
		}

		//
		// Set quality and MaxKeyFrameSpacing on the IWMVideoMediaProps object
		//
		hr = pVideoMediaProps->SetQuality( dwQuality );
		if ( FAILED( hr ) )
		{
			break;
		}

		hr = pVideoMediaProps->SetMaxKeyFrameSpacing( (LONGLONG) dwSecPerKey * 10000000 );
		if ( FAILED( hr ) )
		{
			break;
		}

		//
		// If this profile will be used by writer, we should set the properties
		// on the stream configuration
		//
		if ( SUCCEEDED( pStreamConfig->QueryInterface( IID_IWMPropertyVault, (void**)&pPropertyVault ) ) )
		{
			assert( pPropertyVault );
			hr = pPropertyVault->SetProperty( g_wszVBREnabled, WMT_TYPE_BOOL, (BYTE*) &fIsVBR, sizeof( fIsVBR ) );
			if ( FAILED( hr ) )
			{
				break;
			}

			switch( vbrMode )
			{
			case VBR_QUALITYBASED:
				//
				// Only the quality needs to be set
				//
				hr = pPropertyVault->SetProperty( g_wszVBRQuality, WMT_TYPE_DWORD, (BYTE*) &dwVBRQuality, sizeof( DWORD ) );
				break;

			case VBR_CONSTRAINED:
				//
				// The peak bitrate and, optionally, max buffer window need to be set
				//
				hr = pPropertyVault->SetProperty( g_wszVBRBitrateMax, WMT_TYPE_DWORD, (BYTE*) &dwMaxBitrate, sizeof( DWORD ) );
				if ( FAILED( hr ) )
				{
					break;
				}

				if( dwMaxBufferWindow != 0 )
				{
					hr = pPropertyVault->SetProperty( g_wszVBRBufferWindowMax, WMT_TYPE_DWORD, (BYTE*) &dwMaxBufferWindow, sizeof( DWORD ) );
				}
				break;

			case VBR_UNCONSTRAINED:
				break;

			case VBR_OFF:
				break;

			default:
				hr = E_FAIL;
				break;
			}
			if ( FAILED( hr ) )
			{
				break;
			}

			SAFE_RELEASE( pPropertyVault );
		}
		/*
		hr = SetStreamLanguage( pStreamConfig, dwLanguage );
		if ( FAILED( hr ) )
		{
		break;
		}
		*/
		//
		// Return the pStreamConfig to the caller
		//
		SAFE_ADDREF( pStreamConfig );
		*ppStreamConfig = pStreamConfig;
	}
	while ( FALSE );

	SAFE_RELEASE( pStreamConfig );
	SAFE_RELEASE( pPropertyVault );
	SAFE_ARRAYDELETE( pMediaType );
	SAFE_RELEASE( pVideoMediaProps );

	return hr;
}

//------------------------------------------------------------------------------
// Name: CreateMediatypeForFormat()
// Desc: Initializes a WM_MEDIA_TYPE for a codec format, setting VBR attributes.
//------------------------------------------------------------------------------
STDMETHODIMP CreateMediatypeForFormat( Twmv9dll *dll,
									  WM_MEDIA_TYPE** ppmtDestination,
									  IWMCodecInfo3* pCodecInfo3,
									  IWMStreamConfig** ppFormatConfig,
									  GUID guidCodecType,
									  DWORD dwCodecIndex,
									  DWORD dwFormatIndex,
									  BOOL fIsVBR,
									  DWORD dwVBRPasses )
{
	HRESULT hr = S_OK;
	IWMProfileManager* pProfileManager = NULL;
	IWMStreamConfig* pStreamConfig = NULL;
	IWMMediaProps* pMediaProps = NULL;

	if ( !ppmtDestination )
	{
		return E_INVALIDARG;
	}

	do
	{
		*ppmtDestination = NULL;

		//
		// Make sure the pCodecInfo3 exists, and there's an outstanding reference
		//
		hr = EnsureIWMCodecInfo3( dll,&pCodecInfo3 );
		if ( FAILED( hr ) )
		{
			break;
		}
		assert( pCodecInfo3 );

		//
		// Set the VBR settings appropriately
		//
		hr = SetCodecVBRSettings( pCodecInfo3,
			guidCodecType,
			dwCodecIndex,
			fIsVBR,
			dwVBRPasses );
		if ( FAILED( hr ) )
		{
			break;
		}

		//
		// Call the version that doesn't set the VBR attributes
		//
		hr = CreateMediatypeForFormat( dll,
			ppmtDestination,
			pCodecInfo3,
			ppFormatConfig,
			guidCodecType,
			dwCodecIndex,
			dwFormatIndex );
		if ( FAILED( hr ) )
		{
			break;
		}
		assert( *ppmtDestination );
	}
	while ( FALSE );

	if ( FAILED( hr ) )
	{
		SAFE_ARRAYDELETE( (*ppmtDestination) );
	}

	SAFE_RELEASE( pCodecInfo3 );
	SAFE_RELEASE( pProfileManager );
	SAFE_RELEASE( pStreamConfig );
	SAFE_RELEASE( pMediaProps );

	return hr;
}


//------------------------------------------------------------------------------
// Name: CreateMediatypeForFormat() (Overloaded)
// Desc: Initializes a WM_MEDIA_TYPE for a codec format, without setting
//       VBR attributes.
//------------------------------------------------------------------------------
STDMETHODIMP CreateMediatypeForFormat( Twmv9dll *dll,
									  WM_MEDIA_TYPE** ppmtDestination,
									  IWMCodecInfo3* pCodecInfo3,
									  IWMStreamConfig** ppFormatConfig,
									  GUID guidCodecType,
									  DWORD dwCodecIndex,
									  DWORD dwFormatIndex )
{
	HRESULT hr = S_OK;
	IWMProfileManager* pProfileManager = NULL;
	IWMStreamConfig* pFormatConfig = NULL;
	IWMMediaProps* pMediaProps = NULL;
	DWORD dwMediaTypeLength;

	if ( !ppmtDestination )
	{
		return E_INVALIDARG;
	}

	do
	{
		*ppmtDestination = NULL;

		//
		// Make sure the pCodecInfo3 exists, and there's an outstanding reference
		//
		hr = EnsureIWMCodecInfo3(dll, &pCodecInfo3 );
		if ( FAILED( hr ) )
		{
			break;
		}
		assert( pCodecInfo3 );

		//
		// Get the stream configuration for the given format
		//
		hr = pCodecInfo3->GetCodecFormat( guidCodecType, dwCodecIndex, dwFormatIndex, &pFormatConfig );
		if ( FAILED( hr ) )
		{
			break;
		}
		assert( pFormatConfig );

		//
		// Get the media type for the requested format
		//
		hr = pFormatConfig->QueryInterface( IID_IWMMediaProps, (void**) &pMediaProps );
		if ( FAILED( hr ) )
		{
			break;
		}
		assert( pMediaProps );

		dwMediaTypeLength = 0;
		hr = pMediaProps->GetMediaType( NULL, &dwMediaTypeLength );
		if( FAILED( hr ) )
		{
			break;
		}

		*ppmtDestination = (WM_MEDIA_TYPE *) new BYTE[ dwMediaTypeLength ];
		if( !*ppmtDestination )
		{
			hr = E_OUTOFMEMORY;
			break;
		}

		hr = pMediaProps->GetMediaType( *ppmtDestination, &dwMediaTypeLength );
		if( FAILED( hr ) )
		{
			break;
		}
		assert( *ppmtDestination );

		if ( ppFormatConfig )
		{
			SAFE_ADDREF( pFormatConfig );
			*ppFormatConfig = pFormatConfig;
		}
	}
	while ( FALSE );

	if ( FAILED( hr ) )
	{
		SAFE_ARRAYDELETE( *ppmtDestination );
	}

	SAFE_RELEASE( pCodecInfo3 );
	SAFE_RELEASE( pProfileManager );
	SAFE_RELEASE( pFormatConfig );
	SAFE_RELEASE( pMediaProps );

	return hr;
}


//------------------------------------------------------------------------------
// Name: WriteProfileAsPRX()
// Desc: Writes a profile to a PRX file.
//------------------------------------------------------------------------------
STDMETHODIMP WriteProfileAsPRX( LPCTSTR tszFilename, LPCWSTR wszProfileData, DWORD dwProfileDataLength )
{
	HANDLE hFile = NULL;
	HRESULT hr = S_OK;
	DWORD dwBytesWritten;
	BOOL bResult;

	assert( tszFilename );
	assert( wszProfileData );
	assert( 0 != dwProfileDataLength );

	do
	{
		//
		// Create the file, overwriting any existing file
		//
		hFile = CreateFile( tszFilename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
		if ( INVALID_HANDLE_VALUE == hFile )
		{
			hr = HRESULT_FROM_WIN32( GetLastError() );
			break;
		}

		//
		// Write the profile data to the file
		//
		bResult = WriteFile( hFile, wszProfileData, dwProfileDataLength, &dwBytesWritten, NULL );
		if ( !bResult )
		{
			hr = HRESULT_FROM_WIN32( GetLastError() );
			break;
		}
	}
	while ( FALSE );

	//
	// Close the file, if it was opened successfully
	//
	SAFE_CLOSEHANDLE( hFile );

	return hr;
}


//------------------------------------------------------------------------------
// Name: AddSMPTEExtensionToStream()
// Desc: Add a data unit extension for SMPTE time code.
//------------------------------------------------------------------------------
STDMETHODIMP AddSMPTEExtensionToStream( IWMStreamConfig* pStream )
{
	HRESULT hr = S_OK;
	IWMStreamConfig2* pStreamConfig2 = NULL;

	if ( !pStream )
	{
		return E_INVALIDARG;
	}

	do
	{
		//
		// Get the IWMStreamConfig2 interface
		//
		hr = pStream->QueryInterface( IID_IWMStreamConfig2, (void**) &pStreamConfig2 );
		if ( FAILED( hr ) )
		{
			break;
		}
		assert( pStreamConfig2 );

		//
		// Add SMPTE extension
		//
		hr = pStreamConfig2->AddDataUnitExtension( WM_SampleExtensionGUID_Timecode,
			sizeof( WMT_TIMECODE_EXTENSION_DATA ),
			NULL,
			0 );
		if ( FAILED( hr ) )
		{
			break;
		}
	}
	while ( FALSE );

	SAFE_RELEASE( pStreamConfig2 );

	return hr;
}



//------------------------------------------------------------------------------
// Name: GetUncompressedWaveFormatCount()
// Desc: Returns the number of uncompressed wave formats, four for each
//       frequency in the WaveFrequency array.
//------------------------------------------------------------------------------
STDMETHODIMP GetUncompressedWaveFormatCount( DWORD * pdwCount )
{
	*pdwCount = 4 * sizeof( WaveFrequency ) / sizeof( WaveFrequency[0] );

	return S_OK;
}


//------------------------------------------------------------------------------
// Name: GetUncompressedWaveFormat()
// Desc: Retrieves wave format parameters by index.
//------------------------------------------------------------------------------
STDMETHODIMP GetUncompressedWaveFormat( DWORD dwIndex,
									   DWORD * pdwSamplesPerSecond,
									   WORD * pwNumChannels,
									   WORD * pwBitsPerSample )
{
	if ( NULL == pdwSamplesPerSecond
		|| NULL == pwNumChannels
		|| NULL == pwBitsPerSample )
	{
		return E_POINTER;
	}

	DWORD dwCount;
	HRESULT hr;

	hr = GetUncompressedWaveFormatCount( &dwCount );
	if ( FAILED( hr ) )
	{
		return hr;
	}

	if ( dwIndex >= dwCount )
	{
		return E_INVALIDARG;
	}

	*pdwSamplesPerSecond = WaveFrequency[ dwIndex / 4 ];

	switch ( dwIndex % 4 )
	{
	case 0:
		*pwBitsPerSample = 8;
		*pwNumChannels = 1;
		break;
	case 1:
		*pwBitsPerSample = 8;
		*pwNumChannels = 2;
		break;
	case 2:
		*pwBitsPerSample = 16;
		*pwNumChannels = 1;
		break;
	case 3:
		*pwBitsPerSample = 16;
		*pwNumChannels = 2;
		break;
	}

	return S_OK;
}


//------------------------------------------------------------------------------
// Name: GetUncompressedPixelFormatCount()
// Desc: Returns the number of formats in the PixelFormats array.
//------------------------------------------------------------------------------
STDMETHODIMP GetUncompressedPixelFormatCount( DWORD * pdwCount )
{
	*pdwCount = sizeof( PixelFormats ) / sizeof( PixelFormats[0] );

	return S_OK;
}


//------------------------------------------------------------------------------
// Name: GetUncompressedPixelFormat()
// Desc: Retrieves pixel format parameters by index.
//------------------------------------------------------------------------------
STDMETHODIMP GetUncompressedPixelFormat( DWORD dwIndex,
										GUID * pguidFormat,
										DWORD * pdwFourCC,
										WORD * pwBitsPerPixel )
{
	if ( NULL == pguidFormat
		|| NULL == pdwFourCC
		|| NULL == pwBitsPerPixel )
	{
		return E_POINTER;
	}

	DWORD dwCount = sizeof( PixelFormats ) / sizeof( PixelFormats[0] );

	if ( dwIndex > dwCount )
	{
		return E_INVALIDARG;
	}

	*pguidFormat = *PixelFormats[ dwIndex ].guidFormat;
	*pdwFourCC = PixelFormats[ dwIndex ].dwFourCC;
	*pwBitsPerPixel = PixelFormats[ dwIndex ].wBitsPerPixel;

	return S_OK;
}
